package network;

//import gzip.GZIPInputStream;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.Vector;

import javax.microedition.io.Connector;
import javax.microedition.io.SocketConnection;

public class Session_ME implements ISession {
    protected static Session_ME instance = new Session_ME();

    public static Session_ME gI() {
        return instance;
    }

    private DataOutputStream dos;
    public DataInputStream dis;
    public IMessageHandler messageHandler;
    private SocketConnection sc;
    public boolean connected, connecting;
    private final Sender sender = new Sender();
    public Thread initThread;
    public Thread collectorThread;
    public Thread sendThread;
    public int sendByteCount;
    public int recvByteCount;

    public boolean isConnected() {
        return connected;
    }

    public void setHandler(IMessageHandler messageHandler) {
        this.messageHandler = messageHandler;
    }

    public void connect(String host, int port) {
        if (connected || connecting) {
            return;
        } else {
            sc = null;
            initThread = new Thread(new NetworkInit(host, port));
            initThread.start();
        }
    }

    class NetworkInit implements Runnable {
        private final String host;
        private final int port;

        NetworkInit(String host, int port) {
            this.host = host;
            this.port = port;
        }

        public void run() {
            connecting = true;
            Thread.currentThread().setPriority(Thread.MIN_PRIORITY);
            connected = true;
            try {
                doConnect(host, port);
            } catch (Exception ex) {
                ex.printStackTrace();
                if (messageHandler != null) {
                    close();
                    messageHandler.onConnectionFail();
                }
            }
        }

        public void doConnect(String IP, int P) throws IOException {
            sc = (SocketConnection) Connector.open("socket://" + IP + ":" + P);
            dos = sc.openDataOutputStream();
            dis = sc.openDataInputStream();
            new Thread(sender).start();
            collectorThread = new Thread(new MessageCollector());
            collectorThread.start();
            connecting = false;
            messageHandler.onConnectOK();
        }
    }

    public void sendMessage(Message message) {
        sender.AddMessage(message);
    }

    private synchronized void doSendMessage(Message m) throws IOException {
        byte[] data = m.getData();
        try {
            dos.writeByte(m.type);
            dos.writeShort(m.command);
            if (data != null) {
                dos.writeShort(data.length);
                dos.write(data);
                sendByteCount += (5 + data.length);
            } else {
                dos.writeShort(0);
                sendByteCount += 5;
            }
            dos.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private class Sender implements Runnable {
        private final Vector sendingMessage;

        public Sender() {
            sendingMessage = new Vector();
        }

        public void AddMessage(Message message) {
            sendingMessage.addElement(message);
        }

        public void run() {
            try {
                while (connected) {
                    while (sendingMessage.size() > 0) {
                        Message m = (Message) sendingMessage.elementAt(0);
                        sendingMessage.removeElementAt(0);
                        doSendMessage(m);
                    }
                    try {
                        Thread.sleep(10);
                    } catch (InterruptedException e) {
                    }
                }

            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    class MessageCollector implements Runnable {
        public void run() {
            network.Message message;
            try {
                while (isConnected()) {
                    message = readMessage();
                    if (message != null) {
                        try {
                            messageHandler.onMessage(message);
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    } else {
                        break;
                    }
                }
            } catch (Exception ex) {
            }
            if (connected) {
                if (messageHandler != null) {
                    messageHandler.onDisconnected();
                }
                if (sc != null)
                    cleanNetwork();
            }
        }

        private Message readMessage() throws Exception {
            // read type of message
            int type = dis.readUnsignedByte();
            // read message command
            int cmd = dis.readUnsignedShort();
            // read size of data
            int size = dis.readUnsignedShort();

            byte data[] = new byte[size];
            int len = 0;
            int byteRead = 0;
            while (len != -1 && byteRead < size) {
                len = dis.read(data, byteRead, size - byteRead);
                if (len > 0) {
                    byteRead += len;
                }
            }
            recvByteCount += (5 + byteRead);
            Message msg = new Message(type, cmd, data);
            return msg;
        }
    }

    public void close() {
        cleanNetwork();
    }

    private void cleanNetwork() {
        try {
            connected = false;
            connecting = false;
            if (sc != null) {
                sc.close();
                sc = null;
            }
            if (dos != null) {
                dos.close();
                dos = null;
            }
            if (dis != null) {
                dis.close();
                dis = null;
            }
            sendThread = null;
            collectorThread = null;
            // if (initThread != null && initThread.isAlive()) {
            // initThread.interrupt();
            // initThread = null;
            // }
            System.gc();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
